<script setup lang="ts">
import type { DataTableColumns } from "naive-ui";
import { computed, h, ref, watch } from "vue";
import {
	NButton,
	NCard,
	NDataTable,
	NDivider,
	NInput,
	NList,
	NListItem,
	NModal,
	NPopconfirm,
	NSpace,
	NTabPane,
	NTabs,
	NThing,
	useMessage,
} from "naive-ui";
import PromptRecommend from "../../../assets/recommend.json";
import { SvgIcon } from "..";
import { usePromptStore } from "@/store";
import { useBasicLayout } from "@/hooks/useBasicLayout";
import { t } from "@/locales";

interface DataProps {
	renderKey: string;
	renderValue: string;
	key: string;
	value: string;
}

interface Props {
	visible: boolean;
}

interface Emit {
	(e: "update:visible", visible: boolean): void;
}

const props = defineProps<Props>();

const emit = defineEmits<Emit>();

const message = useMessage();

const show = computed({
	get: () => props.visible,
	set: (visible: boolean) => emit("update:visible", visible),
});

const showModal = ref(false);

const importLoading = ref(false);
const exportLoading = ref(false);

const searchValue = ref<string>("");

// 移动端自适应相关
const { isMobile } = useBasicLayout();

const promptStore = usePromptStore();

// Prompt在线导入推荐List,根据部署者喜好进行修改(assets/recommend.json)
const promptRecommendList = PromptRecommend;
const promptList = ref<any>(promptStore.promptList);

// 用于添加修改的临时prompt参数
const tempPromptKey = ref("");
const tempPromptValue = ref("");

// Modal模式，根据不同模式渲染不同的Modal内容
const modalMode = ref("");

// 这个是为了后期的修改Prompt内容考虑，因为要针对无uuid的list进行修改，且考虑到不能出现标题和内容的冲突，所以就需要一个临时item来记录一下
const tempModifiedItem = ref<any>({});

// 添加修改导入都使用一个Modal, 临时修改内容占用tempPromptKey,切换状态前先将内容都清楚
const changeShowModal = (
	mode: "add" | "modify" | "local_import",
	selected = { key: "", value: "" }
) => {
	if (mode === "add") {
		tempPromptKey.value = "";
		tempPromptValue.value = "";
	} else if (mode === "modify") {
		tempModifiedItem.value = { ...selected };
		tempPromptKey.value = selected.key;
		tempPromptValue.value = selected.value;
	} else if (mode === "local_import") {
		tempPromptKey.value = "local_import";
		tempPromptValue.value = "";
	}
	showModal.value = !showModal.value;
	modalMode.value = mode;
};

// 在线导入相关
const downloadURL = ref("");
const downloadDisabled = computed(() => downloadURL.value.trim().length < 1);
const setDownloadURL = (url: string) => {
	downloadURL.value = url;
};

// 控制 input 按钮
const inputStatus = computed(
	() =>
		tempPromptKey.value.trim().length < 1 ||
		tempPromptValue.value.trim().length < 1
);

// Prompt模板相关操作
const addPromptTemplate = () => {
	for (const i of promptList.value) {
		if (i.key === tempPromptKey.value) {
			message.error(t("store.addRepeatTitleTips"));
			return;
		}
		if (i.value === tempPromptValue.value) {
			message.error(
				t("store.addRepeatContentTips", { msg: tempPromptKey.value })
			);
			return;
		}
	}
	promptList.value.unshift({
		key: tempPromptKey.value,
		value: tempPromptValue.value,
	} as never);
	message.success(t("common.addSuccess"));
	changeShowModal("add");
};

const modifyPromptTemplate = () => {
	let index = 0;

	// 通过临时索引把待修改项摘出来
	for (const i of promptList.value) {
		if (
			i.key === tempModifiedItem.value.key &&
			i.value === tempModifiedItem.value.value
		)
			break;
		index = index + 1;
	}

	const tempList = promptList.value.filter((_: any, i: number) => i !== index);

	// 搜索有冲突的部分
	for (const i of tempList) {
		if (i.key === tempPromptKey.value) {
			message.error(t("store.editRepeatTitleTips"));
			return;
		}
		if (i.value === tempPromptValue.value) {
			message.error(t("store.editRepeatContentTips", { msg: i.key }));
			return;
		}
	}

	promptList.value = [
		{ key: tempPromptKey.value, value: tempPromptValue.value },
		...tempList,
	] as never;
	message.success(t("common.editSuccess"));
	changeShowModal("modify");
};

const deletePromptTemplate = (row: { key: string; value: string }) => {
	promptList.value = [
		...promptList.value.filter(
			(item: { key: string; value: string }) => item.key !== row.key
		),
	] as never;
	message.success(t("common.deleteSuccess"));
};

const clearPromptTemplate = () => {
	promptList.value = [];
	message.success(t("common.clearSuccess"));
};

const importPromptTemplate = (from = "online") => {
	try {
		const jsonData = JSON.parse(tempPromptValue.value);
		let key = "";
		let value = "";
		// 可以扩展加入更多模板字典的key
		if ("key" in jsonData[0]) {
			key = "key";
			value = "value";
		} else if ("act" in jsonData[0]) {
			key = "act";
			value = "prompt";
		} else {
			// 不支持的字典的key防止导入 以免破坏prompt商店打开
			message.warning("prompt key not supported.");
			throw new Error("prompt key not supported.");
		}

		for (const i of jsonData) {
			if (!(key in i) || !(value in i)) throw new Error(t("store.importError"));
			let safe = true;
			for (const j of promptList.value) {
				if (j.key === i[key]) {
					message.warning(t("store.importRepeatTitle", { msg: i[key] }));
					safe = false;
					break;
				}
				if (j.value === i[value]) {
					message.warning(t("store.importRepeatContent", { msg: i[key] }));
					safe = false;
					break;
				}
			}
			if (safe)
				promptList.value.unshift({ key: i[key], value: i[value] } as never);
		}
		message.success(t("common.importSuccess"));
	} catch {
		message.error("JSON 格式错误，请检查 JSON 格式");
	}
	if (from === "local") showModal.value = !showModal.value;
};

// 模板导出
const exportPromptTemplate = () => {
	exportLoading.value = true;
	const jsonDataStr = JSON.stringify(promptList.value);
	const blob = new Blob([jsonDataStr], { type: "application/json" });
	const url = URL.createObjectURL(blob);
	const link = document.createElement("a");
	link.href = url;
	link.download = "ChatGPTPromptTemplate.json";
	link.click();
	URL.revokeObjectURL(url);
	exportLoading.value = false;
};

// 模板在线导入
const downloadPromptTemplate = async () => {
	try {
		importLoading.value = true;
		const response = await fetch(downloadURL.value);
		const jsonData = await response.json();
		if ("key" in jsonData[0] && "value" in jsonData[0])
			tempPromptValue.value = JSON.stringify(jsonData);
		if ("act" in jsonData[0] && "prompt" in jsonData[0]) {
			const newJsonData = jsonData.map(
				(item: { act: string; prompt: string }) => {
					return {
						key: item.act,
						value: item.prompt,
					};
				}
			);
			tempPromptValue.value = JSON.stringify(newJsonData);
		}
		importPromptTemplate();
		downloadURL.value = "";
	} catch {
		message.error(t("store.downloadError"));
		downloadURL.value = "";
	} finally {
		importLoading.value = false;
	}
};

// 移动端自适应相关
const renderTemplate = () => {
	const [keyLimit, valueLimit] = isMobile.value ? [10, 30] : [15, 50];

	return promptList.value.map((item: { key: string; value: string }) => {
		return {
			renderKey:
				item.key.length <= keyLimit
					? item.key
					: `${item.key.substring(0, keyLimit)}...`,
			renderValue:
				item.value.length <= valueLimit
					? item.value
					: `${item.value.substring(0, valueLimit)}...`,
			key: item.key,
			value: item.value,
		};
	});
};

const pagination = computed(() => {
	const [pageSize, pageSlot] = isMobile.value ? [6, 5] : [7, 15];
	return {
		pageSize,
		pageSlot,
	};
});

// table相关
const createColumns = (): DataTableColumns<DataProps> => {
	return [
		{
			title: t("store.title"),
			key: "renderKey",
		},
		{
			title: t("store.description"),
			key: "renderValue",
		},
		{
			title: t("common.action"),
			key: "actions",
			width: 100,
			align: "center",
			render(row) {
				return h(
					"div",
					{ class: "flex items-center flex-col gap-2" },
					{
						default: () => [
							h(
								NButton,
								{
									tertiary: true,
									size: "small",
									type: "info",
									onClick: () => changeShowModal("modify", row),
								},
								{ default: () => t("common.edit") }
							),
							h(
								NButton,
								{
									tertiary: true,
									size: "small",
									type: "error",
									onClick: () => deletePromptTemplate(row),
								},
								{ default: () => t("common.delete") }
							),
						],
					}
				);
			},
		},
	];
};

const columns = createColumns();

watch(
	() => promptList,
	() => {
		promptStore.updatePromptList(promptList.value);
	},
	{ deep: true }
);

const dataSource = computed(() => {
	const data = renderTemplate();
	const value = searchValue.value;
	if (value && value !== "") {
		return data.filter((item: DataProps) => {
			return item.renderKey.includes(value) || item.renderValue.includes(value);
		});
	}
	return data;
});
</script>

<template>
	<NModal
		v-model:show="show"
		style="width: 90%; max-width: 900px"
		preset="card"
	>
		<div class="space-y-4">
			<NTabs type="segment" animated>
				<NTabPane name="local" :tab="$t('store.local')">
					<div
						class="flex gap-3 mb-4"
						:class="[isMobile ? 'flex-col' : 'flex-row justify-between']"
					>
						<div class="flex items-center space-x-4">
							<NButton
								type="primary"
								size="small"
								@click="changeShowModal('add')"
							>
								{{ $t("common.add") }}
							</NButton>
							<NButton size="small" @click="changeShowModal('local_import')">
								{{ $t("common.import") }}
							</NButton>
							<NButton
								size="small"
								:loading="exportLoading"
								@click="exportPromptTemplate()"
							>
								{{ $t("common.export") }}
							</NButton>
							<N @positive-click="clearPromptTemplate">
								<template #trigger>
									<NButton size="small">
										{{ $t("common.clear") }}
									</NButton>
								</template>
								{{ $t("store.clearStoreConfirm") }}
							</N>
						</div>
						<div class="flex items-center">
							<NInput v-model:value="searchValue" style="width: 100%" />
						</div>
					</div>
					<NDataTable
						v-if="!isMobile"
						:max-height="400"
						:columns="columns"
						:data="dataSource"
						:pagination="pagination"
						:bordered="false"
					/>
					<NList v-if="isMobile" style="max-height: 400px; overflow-y: auto">
						<NListItem v-for="(item, index) of dataSource" :key="index">
							<NThing :title="item.renderKey" :description="item.renderValue" />
							<template #suffix>
								<div class="flex flex-col items-center gap-2">
									<NButton
										tertiary
										size="small"
										type="info"
										@click="changeShowModal('modify', item)"
									>
										{{ t("common.edit") }}
									</NButton>
									<NButton
										tertiary
										size="small"
										type="error"
										@click="deletePromptTemplate(item)"
									>
										{{ t("common.delete") }}
									</NButton>
								</div>
							</template>
						</NListItem>
					</NList>
				</NTabPane>
				<NTabPane name="download" :tab="$t('store.online')">
					<p class="mb-4">
						{{ $t("store.onlineImportWarning") }}
					</p>
					<div class="flex items-center gap-4">
						<NInput v-model:value="downloadURL" placeholder="" />
						<NButton
							strong
							secondary
							:disabled="downloadDisabled"
							:loading="importLoading"
							@click="downloadPromptTemplate()"
						>
							{{ $t("common.download") }}
						</NButton>
					</div>
					<NDivider />
					<div class="max-h-[360px] overflow-y-auto space-y-4">
						<NCard
							v-for="info in promptRecommendList"
							:key="info.key"
							:title="info.key"
							:bordered="true"
							embedded
						>
							<p
								class="overflow-hidden text-ellipsis whitespace-nowrap"
								:title="info.desc"
							>
								{{ info.desc }}
							</p>
							<template #footer>
								<div class="flex items-center justify-end space-x-4">
									<NButton text>
										<a :href="info.url" target="_blank">
											<SvgIcon class="text-xl" icon="ri:link" />
										</a>
									</NButton>
									<NButton text @click="setDownloadURL(info.downloadUrl)">
										<SvgIcon class="text-xl" icon="ri:add-fill" />
									</NButton>
								</div>
							</template>
						</NCard>
					</div>
				</NTabPane>
			</NTabs>
		</div>
	</NModal>

	<NModal
		v-model:show="showModal"
		style="width: 90%; max-width: 600px"
		preset="card"
	>
		<NSpace v-if="modalMode === 'add' || modalMode === 'modify'" vertical>
			{{ t("store.title") }}
			<NInput v-model:value="tempPromptKey" />
			{{ t("store.description") }}
			<NInput v-model:value="tempPromptValue" type="textarea" />
			<NButton
				block
				type="primary"
				:disabled="inputStatus"
				@click="
					() => {
						modalMode === 'add' ? addPromptTemplate() : modifyPromptTemplate();
					}
				"
			>
				{{ t("common.confirm") }}
			</NButton>
		</NSpace>
		<NSpace v-if="modalMode === 'local_import'" vertical>
			<NInput
				v-model:value="tempPromptValue"
				:placeholder="t('store.importPlaceholder')"
				:autosize="{ minRows: 3, maxRows: 15 }"
				type="textarea"
			/>
			<NButton
				block
				type="primary"
				:disabled="inputStatus"
				@click="
					() => {
						importPromptTemplate('local');
					}
				"
			>
				{{ t("common.import") }}
			</NButton>
		</NSpace>
	</NModal>
</template>
